package com.glq1218.background.config;

import com.glq1218.authentication.filter.JwtAuthenticationFilter;
import com.glq1218.common.util.ServletUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.Arrays;
import java.util.stream.Stream;

/**
 * @author glq1218
 */
@Configuration
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    private final JwtAuthenticationFilter jwtAuthFilter;

    /**
     * 放行Swagger
     */
    public static final String[] SWAGGER_WHITELIST = {
            "/swagger-ui/**",
            "/v3/api-docs/**",
            "/swagger-resources/**",
            "/webjars/**"
    };

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                // 允许跨域
                .cors().and()
                // 关闭表单登录
                .formLogin().disable()
                // 关闭默认登出
                .logout().disable()
                // 关闭csrf
                .csrf().disable()
                // 不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeHttpRequests()
                // 允许匿名访问
                .requestMatchers(requestMatchersAnonymous()).anonymous()
                // 静态资源不需要任何限制
                .requestMatchers(HttpMethod.GET, requestMatchersPermitAll()).permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated().and()
                // 指定异常处理器
                .exceptionHandling()
                // 认账异常处理
                .authenticationEntryPoint(authenticationEntryPoint())
                // 授权异常处理
                .accessDeniedHandler(accessDeniedHandler()).and()
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    /**
     * 不需要任何限制url（静态资源）
     *
     * @return 字符串数组
     */
    private String[] requestMatchersPermitAll() {
        String[] permitAll = new String[]{
                "/*.html",
                "/*.ico",
                "/*/*.html",
                "/*/*.css",
                "/ws/**",
        };
        return Stream.concat(Arrays.stream(SWAGGER_WHITELIST), Arrays.stream(permitAll)).toArray(String[]::new);
    }

    /**
     * 允许匿名访问url
     *
     * @return 字符串数组
     */
    private String[] requestMatchersAnonymous() {
        return new String[]{
                "/api/auth/**",
                "/system/upload/**"
        };
    }

    /**
     * 密码加密规则 强散列哈希加密实现(密码加密算法)
     *
     * @return 密码加密规则
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义认证失败处理器
     *
     * @return 认证失败处理
     */
    @Bean
    public AuthenticationEntryPoint authenticationEntryPoint() {
        return (request, response, authException) -> {
            authException.printStackTrace();
            if (authException instanceof BadCredentialsException) {
                ServletUtils.sendMsg(response, HttpServletResponse.SC_UNAUTHORIZED, "密码不正确");
                return;
            }
            // 当用户尝试访问安全的REST资源而不提供任何凭据时，将调用此方法发送401 响应
            ServletUtils.sendMsg(response, HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
        };
    }

    /**
     * 自定义授权异常处理
     *
     * @return 授权异常处理
     */
    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return (request, response, accessDeniedException) -> {
            accessDeniedException.printStackTrace();
            // 当用户在没有授权的情况下访问受保护的REST资源时，将调用此方法发送403 Forbidden响应
            ServletUtils.sendMsg(response, HttpServletResponse.SC_FORBIDDEN, accessDeniedException.getMessage());
        };
    }
}
